---
title: How Keystatic organises your content
summary: Control and flexibility with where your content gets generated.
---
Keystatic has two&nbsp;*concepts*&nbsp;or structures to organise data:&nbsp;`collections`&nbsp;and&nbsp;`singletons`.

Those are defined in the&nbsp;[Keystatic configuration](/docs/configuration).

You get a lot of control and flexibility with *where* your content gets generated, both at the `collection` or `singleton` level, and at the `field` level for certain field types, like images.

### Path configuration

You can define _where_ Keystatic should store collection entries and singletons via the `path` property in the collection/singleton top-level options:

```javascript
// Keystatic config
export default config({
  collections: {
    posts: collection({
      label: 'Posts',
      path: 'content/posts/*/',
      // ...
    })
  },
  singletons: {
    settings: singleton({
      label: 'Settings',
      path: 'content/posts/',
      // ...
   })
  }
})

```

The optional trailing slash `/` on that path has an impact on the content structure - read below for more details on `collection paths` and `singleton paths`.

{% aside icon="⚡️" %}
`collections` require a `*` wildcard part of the path string. 

This will be replaced by the slug of the entry.
{% /aside %}

We will go over core concepts here, but check out the [Path wildcard page](/docs/path-wildcard) for more details and advanced examples.

---

## Collections

The default `path` value, if not specified, will be `{collection-name}/*/`.

### Collection paths ending with a trailing slash `/`

If the path ends with a trailing slash `/`, each entry will be created in its own directory named after the slug:

```yaml
collection-name
└── slug
    ├── index.yaml
    └── other.mdoc
```

Say you create two entries in the `posts` collection, where the `path` is set to `'content/posts/*/'`.

Since there is a trailing slash in the `path`, the generated output will look like so:

```yaml
content
└── posts
    ├── my-first-post
        ├── index.yaml
        ├── other.mdoc
    └── my-second-post
        ├── index.yaml
        └── other.mdoc
```

### Collection paths ending without a trailing slash

If the path does not end with a trailing slash, entries' index files will be created immediately inside the collection directory:

```yaml
collection-name
├── slug.yaml
└── slug
    └── other.mdoc
```

Say you create two entries in the `posts` collection, where the `path` is set to `'content/posts/*'`.

Since there is no trailing slash in the `path`, the generated output will look like so:

```yaml
content
└── posts
    ├── my-first-post.yaml
    └── my-first-post
        └── other.mdoc
    ├── my-second-post.yaml
    └── my-second-post
        └── other.mdoc
```

---

## Singletons

The `path` property for singletons does not contain a `*` wildcard.

If not specified, the default `path` value for singletons will be `{singleton-name}/`.

### Singleton paths ending with a trailing slash `/`

If the path ends with a trailing slash `/`, the singleton's content will be created **inside a directory** named after the singleton:

```yaml
singleton-name
├── index.yaml
└── other.mdoc

```

### Singleton paths ending without a trailing slash

If the path does not end with a trailing slash, the content will be stored **in a file** named after the singleton. 

Additional document fields will be stored inside a directory with the same name:

```yaml
singleton-name.yaml
singleton-name
└── other.mdoc

```

---

## Images output path

You can decide where to store your images independently of the path configuration for a given collection or singleton.

This is useful when you want to have your images in the `public` or `assets` directory, to comply to framework-specific conventions.

```javascript
// In the context of a `posts` collection...
coverImage: fields.image({
  label: "Cover Image",
  directory: "public/images/posts",
}),

```

Regardless of where the `posts` entries are created, the `coverImage` image will be generated in `public/images/posts/{post-slug}`.

---

## Path prefix

If you're in a monorepo, you can use the `storage.pathPrefix` option to scope Keystatic to a specific directory instead of adding a prefix to every `path` option.

For example, this config will look for posts in `somewhere/my-site/content/posts`:

```js
export default config({
  storage: {
    kind: 'github',
    repo: 'my-org/my-repo',
    pathPrefix: 'somewhere/my-site'
  },
  collections: {
    posts: collection({
      label: 'Posts',
      path: 'content/posts/*/',
      // ...
    })
  },
})
```

## Screencast walk-through

This segment of the [Keystatic Mini-Course on YouTube](https://www.youtube.com/playlist?list=PLHrxuCR-0CcSmkyLcmdV7Ruql8DTm644k) also provides context on how the path configuration works:

{% embed
   mediaType="video"
   embedCode="<iframe src=\"https://www.youtube.com/embed/0Zo9q2tcn3k\" title=\"YouTube video player\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" allowfullscreen></iframe>" /%}

